<template>
  <div id="make-zhinengshibie">
    <!-- 目录解析loading遮罩层 -->
    <div v-if="mlLoading" class="loading-mask">
      <div class="dot">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
      </div>
      <span class="loading-text"
        >正在解析书籍内容，马上为您生成目录~
        请保持当前页面开启，避免操作中断。</span
      >
    </div>
    <div class="step-one">
      <div v-show="!loading" class="one-main">
        <!-- 
          文件上传组件：支持上传 PDF 文件到 /dify/upload 接口。
          
          属性说明：
          - ref="upload": 引用 el-upload 组件实例，用于后续操作（如手动触发上传）。
          - action="/dify/upload": 指定文件上传的目标 API 地址。
          - :data="{ bookInfoId: 1 }": 额外的请求参数，这里传递了 bookInfoId=1。
          - :on-success="handleSuccess": 文件上传成功后的回调函数。
          - :on-error="handleError": 文件上传失败后的回调函数。
          - :before-upload="beforeUpload": 在文件上传前执行的钩子函数，通常用于校验文件类型和大小。
          - :limit="1": 限制一次最多只能上传一个文件。
          - accept=".pdf": 只允许选择 PDF 格式的文件。
          - :auto-upload="true": 自动上传，选择文件后立即上传，如果设置为 false，则需要手动调用 upload 方法来触发上传。
        -->
        <el-upload
          ref="upload"
          class="avatar-uploader"
          :key="uploadKey"
          action="/dev/dify/upload"
          :data="{ bookInfoId: 1 }"
          :show-file-list="false"
          :on-success="handleSuccess"
          :before-upload="beforeUpload"
          :on-error="handleError"
          :limit="1"
          accept=".pdf"
          :auto-upload="true"
          :http-request="customUpload"
          drag
        >
          <div class="upload-box">
            <div class="box-one">
              <div class="img-box">
                <img src="@/assets/img/zhishi/upload.png" />
              </div>
              <div class="one-txt">导入本地文档，点击上传或拖拽文件到这里</div>
              <div class="one-subtxt">
                支持PDF（带目录的教材书籍），每个文件不超过 100MB。
              </div>
            </div>
            <div class="box-two">
              <div class="two-txt">上传书籍中确保有目录哦~ 例如：</div>
              <div>
                <img src="@/assets/img/zhishi/upload-info.png" />
              </div>
            </div>
          </div>
        </el-upload>
        <div class="file-list">
          <div v-for="item in PDFList" :key="item.id">
            <div class="file-left">
              <div>
                <img src="@/assets/img/zhishi/pdf.png" alt="PDF 图标" />
              </div>
              <div class="file-title">{{ item.fileName }}</div>
            </div>
            <div class="file-right">
              <div class="file-size">{{ item.fileSize }}</div>
              <div class="file-type">
                <div v-if="item.status === '0'">
                  <img
                    src="@/assets/img/zhishi/weishengcheng.png"
                    alt="未生成图标"
                  />
                  未生成
                </div>
                <div v-else-if="item.status === '1'">
                  <!-- <img
                    src="@/assets/img/zhishi/weishengcheng.png"
                    alt="构建成功图标"
                  > -->
                  <el-icon color="green" size="16">
                    <Check />
                  </el-icon>
                  <span style="color: green">构建成功</span>
                </div>
                <div v-else-if="item.status === '2'">
                  <img
                    src="@/assets/img/zhishi/weishengcheng.png"
                    alt="生成中图标"
                  />
                  生成中
                </div>
                <div v-else-if="item.status === '3'">
                  <img
                    src="@/assets/img/zhishi/weishengcheng.png"
                    alt="生成失败图标"
                  />
                  生成失败
                  <el-tooltip
                    class="box-item"
                    effect="dark"
                    placement="bottom-start"
                  >
                    <template #content>
                      <div style="width: 200px">
                        <!-- 本次生成操作于第20页处中断。后续继续进行生成时，将从第20页起始，直至文档末页。 -->
                        {{ item.taskExpContent }}
                      </div>
                    </template>
                    <img src="@/assets/img/zhishi/tishi.png" alt="提示图标" />
                  </el-tooltip>
                </div>
              </div>
              <!-- 生成图谱按钮 -->
              <div>
                <div
                  class="file-btn"
                  :class="{
                    'disabled-btn': ![0, 3].includes(Number(item.status)),
                  }"
                  @click="handleGenerateGraph(item)"
                >
                  <img src="@/assets/img/zhishi/ai.png" alt="生成图谱图标" />
                  生成课程
                </div>
              </div>
              <div class="flie-close" @click="deletePDF(item)">
                <img src="@/assets/img/zhishi/close.png" alt="关闭图标" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div v-if="loading" class="loading">
      <div class="all-box">
        <div class="loading-img">
          <img src="@/assets/img/zhishi/loading.png" />
        </div>
        <div class="loading-title">AI正在构建您的知识图谱，请保持页面开启</div>
        <div class="loading-subtitle">
          本次生成知识图谱时，知识点应进行增量更新，确保原有数据不被覆盖。
        </div>
      </div>
    </div>
    <el-dialog v-model="dialogVisible" width="550" :before-close="handleClose">
      <div class="my-dialog">
        <div class="my-dialog-title">
          <img src="@/assets/img/zhishi/jiqiren.png" />
          为了提高解析的精确度，请告诉我目录列表在书籍中的页码范围。
        </div>
        <div class="my-dialog-content">
          <div>第</div>
          <div>
            <el-input-number
              v-model="startPage"
              :min="1"
              :max="10"
              :step="1"
              step-strictly
            />
          </div>
          <div>页至第</div>
          <div>
            <el-input-number
              v-model="endPage"
              :min="1"
              :step="1"
              step-strictly
            />
          </div>
          <div>页</div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button color="#155EEF" @click="analysisCourse()">
            确定
          </el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 解析目录弹窗 -->
    <el-dialog
      class="dialog-ai"
      v-model="dialogAI"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      width="1200px"
      top="60px"
      center
    >
      <div class="ai-tip">
        <div class="tip">
          <img src="@/assets/img/ai助手.png" alt="" />
          <span>请确认目录解析完整程度，支持重新解析及手动修改</span>
        </div>
        <div class="reset" @click="resetData()">
          <img src="@/assets/img/reset.png" alt="" />
          <span>重新解析</span>
        </div>
      </div>
      <!-- 全选/反选checkbox -->
      <div class="select-all-container">
        <el-checkbox
          v-model="isSelectAll"
          :indeterminate="isIndeterminate"
          @change="handleSelectAll"
        >
          全选
        </el-checkbox>
      </div>
      <el-tree
        ref="tree"
        class="tree-container"
        :data="treeData"
        node-key="id"
        show-checkbox
        :expand-on-click-node="false"
        :props="defineProps"
        default-expand-all
        @check="updateSelectAllStatus"
      >
        <template v-slot="{ data }">
          <span class="custom-tree-node">
            <span v-if="!data.isEdit" class="node-content">
              <span class="node-title">{{ data.title }}</span>
              <span class="node-page">第{{ data.page || "--" }}页</span>
            </span>
            <div v-else class="edit-inputs">
              <input
                :ref="`title_${data.id}`"
                placeholder="请输入节点名称"
                v-model="currentName"
                @focus="clearSaveTimer"
                @blur="($event) => handleInputBlur($event, data)"
                @keyup.enter="($event) => handleInputBlur($event, data)"
                class="title-input"
              />
              <input
                :ref="`page_${data.id}`"
                placeholder="页码"
                v-model="currentPage"
                @focus="clearSaveTimer"
                @blur="($event) => handlePageBlur($event, data)"
                @keyup.enter="($event) => handlePageBlur($event, data)"
                class="page-input"
                type="text"
              />
            </div>
            <div v-if="!data.isEdit" class="node-actions">
              <button class="add-btn" @click.stop="addNode(data)">添加</button>
              <button class="edit-btn" @click.stop="editNode(data)">
                修改
              </button>
              <button class="delete-btn" @click.stop="deleteNode(data)">
                删除
              </button>
            </div>
          </span>
        </template>
      </el-tree>
      <template #footer>
        <span class="dialog-footer">
          <el-button
            class="footer-btn"
            type="primary"
            round
            @click="confirmGenerate()"
            >确认生成</el-button
          >
        </span>
      </template>
    </el-dialog>
  </div>
</template>
    
<script>
export default {
  data() {
    return {
      courseId: "",
      userInfo: "",
      userId: "",
      websocket: null, // WebSocket实例
      mlLoading: false, // 解析书籍目录loading
      dialogAI: false,
      uploadKey: 0, // 动态 key
      PDFList: [],
      loading: false,
      dialogVisible: false,
      imageUrl: "",
      num: 1,
      fileId: "",
      startPage: 1,
      endPage: 1,
      treeSelects: [], // 选中节点
      treeData: [],
      defineProps: {
        label: "title",
        children: "sections",
      },
      currentName: "", // 当前编辑的节点名称
      currentPage: "", // 当前编辑的页码
      saveTimer: null, // 延迟保存定时器
      timer: null, // 轮询定时器
      isSelectAll: false, // 全选状态
      isIndeterminate: false, // 半选状态
    };
  },
  created() {
    this.courseId = JSON.parse(localStorage.getItem("courseId"));
    this.userInfo = JSON.parse(localStorage.getItem("userInfo"));
    this.userId = this.userInfo.userId;
    this.getPDFList();
  },

  beforeUnmount() {
    // 组件销毁前清理WebSocket连接
    this.closeWebSocket();
    // 清理延迟保存定时器
    this.clearSaveTimer();
    // 清理轮询定时器
    this.clearPolling();
  },
  methods: {
    clearPDFInfo() {
      this.fileId = "";
      this.startPage = 1;
      this.endPage = 1;
    },
    //处理生成图谱按钮的点击事件
    handleGenerateGraph(item) {
      const allowedStatuses = [0, 3]; // 允许的状态：未生成 (0) 和 生成失败 (3)
      if (!allowedStatuses.includes(Number(item.status))) {
        this.$message.warning("当前状态不允许生成课程！");
        return;
      }

      // 如果状态是3（生成失败），直接跳转知识库
      if (Number(item.status) === 3) {
        // 开启目录解析loading
        this.mlLoading = true;
        const url = `/tes/course/auto/make/updateDirectoryTree?fileId=${item.id}`;
        this.$axios
          .post(url, { units: [] })
          .then((res) => {
            if (res.code === 200) {
              // this.getPDFList();
              this.$router.push("/aiTeaching/courseBuild/makeKnowledgeBase");
            } else {
              this.$message.error(res.message);
            }
          })
          .finally(() => {
            this.mlLoading = false;
          });
        return;
      }

      // 其他状态走原来的逻辑
      this.clearPDFInfo();
      this.fileId = item.id;
      this.dialogVisible = true;
    },

    // 初始化websocket
    initWebsocket() {
      if (!this.userId) {
        this.$message.error("用户ID未定义");
        this.closeLoadingAndWebSocket();
        return false;
      }

      try {
        // 修改为localhost地址
        const wsUrl = `ws://182.26.1.220:9999/tes/ws/pdf/${this.userId}`;
        console.log("初始化WebSocket连接:", wsUrl);

        this.websocket = new WebSocket(wsUrl);
        this.websocket.onopen = this.websocketonopen;
        this.websocket.onmessage = this.websocketonmessage;
        this.websocket.onclose = this.websocketonclose;
        this.websocket.onerror = this.websocketonerror;

        return true;
      } catch (error) {
        console.error("WebSocket初始化失败:", error);
        this.$message.error("WebSocket连接初始化失败");
        this.closeLoadingAndWebSocket();
        return false;
      }
    },

    // 打开websocket
    websocketonopen() {
      console.log("WebSocket连接成功");
    },

    // 数据接收
    websocketonmessage(e) {
      console.log("收到WebSocket消息:", e.data);

      try {
        const eData = JSON.parse(e.data);
        console.log("解析后的数据:", eData);

        // 检查返回状态
        if (eData.status == 500) {
          this.$message.error("解析失败，请重新上传文件后重试");
          this.closeLoadingAndWebSocket();
          return;
        }

        // 检查数据完整性
        if (!eData.data || !eData.data.units) {
          this.$message.error("返回数据格式错误");
          this.closeLoadingAndWebSocket();
          return;
        }

        // 转换树形数据
        this.treeData = this.transformData(eData.data);

        this.$message.success("目录解析成功");

        // 解析成功，关闭loading并显示确认弹窗
        this.mlLoading = false;
        this.dialogAI = true;

        // 关闭WebSocket连接
        this.closeWebSocket();
      } catch (error) {
        console.error("处理WebSocket消息失败:", error);
        this.$message.error("数据解析失败");
        this.closeLoadingAndWebSocket();
      }
    },

    // WebSocket关闭事件
    websocketonclose(e) {
      console.log("WebSocket连接已关闭", e);
      // 清理WebSocket实例
      this.websocket = null;
    },

    // WebSocket错误事件
    websocketonerror(error) {
      console.error("WebSocket连接错误:", error);
      this.$message.error("WebSocket连接出现错误");
      this.closeLoadingAndWebSocket();
    },

    // 关闭WebSocket连接
    closeWebSocket() {
      if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
        this.websocket.close();
      }
    },

    // 关闭loading和WebSocket的统一方法
    closeLoadingAndWebSocket() {
      this.mlLoading = false;
      this.closeWebSocket();
    },

    // 树结构转换
    transformData(data) {
      return data.units.map((item, index) => {
        return {
          id: item.id,
          parentId: item.parentId,
          title: item.title,
          page: item.page,
          weidu: item.weidu,
          isEdit: false,
          sections:
            item.sections && item.sections.length > 0
              ? this.transformData({
                  units: item.sections,
                })
              : [],
        };
      });
    },

    // 解析pdf生成课程目录
    analysisCourse() {
      // 参数验证
      if (!this.fileId) {
        this.$message.error("请先选择要解析的文件");
        return;
      }

      if (!this.startPage || !this.endPage) {
        this.$message.error("请输入有效的页码范围");
        return;
      }

      if (this.startPage > this.endPage) {
        this.$message.error("起始页码不能大于结束页码");
        return;
      }

      // 关闭弹窗
      this.dialogVisible = false;

      // 开启目录解析loading
      this.mlLoading = true;

      // 初始化WebSocket连接
      // const wsInitSuccess = this.initWebsocket();
      // if (!wsInitSuccess) {
      //   console.log("webSocket链接失败");
      //   return;
      // }

      const url = `/tes/course/auto/make/startProcessDirectoryTree?fileId=${this.fileId}&startPage=${this.startPage}&endPage=${this.endPage}`;
      this.$axios
        .post(url)
        .then((res) => {
          if (res.code === 200) {
            this.treeData = this.transformData(res.data);
            this.dialogAI = true;
            this.getPDFList();
            // 初始化全选状态
            this.$nextTick(() => {
              this.updateSelectAllStatus();
            });
          } else {
            this.$message.error(res.message);
          }
        })
        .finally(() => {
          this.mlLoading = false;
        });
    },
    // analysisPDF() {
    //   const url = `/dify/startProcess?fileId=${this.fileId}&startPage=${
    //     this.startPage || 5
    //   }&endPage=${this.endPage || 8}`;
    //   this.dialogVisible = false;
    //   this.loading = true;
    //   // 执行生成图谱的逻辑
    //   this.$message.success("正在生成图谱，请稍候...");
    //   this.$axios
    //     .post(url)
    //     .then((response) => {
    //       const res = response;
    //       console.log(res, 1);
    //       if (res.code === 200) {
    //         this.PDFList = res.data;
    //       } else {
    //         this.$message.error(res.msg || "获取失败，请稍后重试");
    //       }
    //       this.loading = false;
    //       this.getPDFList();
    //     })
    //     .catch((error) => {
    //       this.$message.error(error.message || "获取失败，请稍后重试");
    //       this.loading = false;
    //       this.getPDFList();
    //     });
    // },

    //删除PDF文件
    deletePDF(item) {
      const url = `/tes/course/auto/make?id=${item.id}`;
      //删除前确认
      this.$confirm("确定要删除该文件吗？")
        .then(() => {
          this.$axios.delete(url).then((response) => {
            const res = response;
            this.$message.success(res.message || "删除成功");
            this.getPDFList();
          });
        })
        .catch(() => {
          this.$message.info("已取消删除");
        });
    },
    //获取pdf列表
    getPDFList() {
      // const url = "/dify/pdf/history";
      const url = `/tes/course/auto/make?courseId=${this.courseId}`;
      // const params = {
      //   bookInfoId: 1, // 使用 params 传递查询参数
      // };
      this.$axios.get(url).then((response) => {
        const res = response;
        this.PDFList = res.data;
        this.checkPolling();
      });
    },
    // 检查是否需要轮询
    checkPolling() {
      const needPolling = this.PDFList.some((item) => item.status == 2);

      if (needPolling) {
        if (!this.timer) {
          this.startPolling();
        }
      } else {
        this.clearPolling();
      }
    },

    // 开始轮询
    startPolling() {
      this.timer = setInterval(() => {
        this.getPDFList();
      }, 30000); // 30秒轮询一次
    },

    // 清除轮询
    clearPolling() {
      if (this.timer) {
        clearInterval(this.timer);
        this.timer = null;
      }
    },

    //自定义上传
    customUpload(options) {
      const { file, data } = options;
      // 创建 FormData 对象
      const formData = new FormData();
      formData.append("file", file); // 添加文件

      for (const key in data) {
        formData.append(key, data[key]); // 添加额外参数（如 bookInfoId）
      }
      // 显示"上传中"提示
      const loading = this.$loading({
        lock: true,
        message: "上传中，请稍候...",
        background: "rgba(0, 0, 0, 0.7)",
      });
      // 使用全局挂载的 $axios 实例发起上传请求
      this.$axios
        .post(
          `/tes/course/auto/make/upload?courseId=${this.courseId}`,
          formData
        )
        .then((response) => {
          this.handleSuccess(response, file); // 调用成功回调
          this.getPDFList();
        })
        .catch((error) => {
          this.handleError(error, file); // 调用失败回调
        })
        .finally(() => {
          // 关闭"上传中"提示
          loading.close();
        });
    },
    handleSuccess(res, file) {
      console.log("上传成功：", res);
      this.$message.success("上传成功!");
      this.uploadKey += 1; // 修改 key 值，强制重新渲染组件
    },
    beforeUpload(file) {
      // 检查是否为 PDF 文件
      const isPDF = file.type === "application/pdf";
      // 检查文件大小是否小于 100MB
      const isLt100M = file.size / 1024 / 1024 < 100;
      if (!isPDF) {
        this.$message.error("只能上传 PDF 文件！");
      }
      if (!isLt100M) {
        this.$message.error("上传文件大小不能超过 100MB！");
      }

      return isPDF && isLt100M; // 返回校验结果
    },
    handleError() {
      // this.$message.error("上传失败，请稍后再试!");
      this.uploadKey += 1; // 修改 key 值，强制重新渲染组件
    },
    handleClose() {
      this.dialogVisible = false;
    },

    // 编辑节点
    editNode(data) {
      this.currentName = data.title || data.label;
      this.currentPage = data.page || "";
      data.isEdit = true;
      this.$nextTick(() => {
        // 聚焦到标题输入框
        const titleInputElement = this.$refs[`title_${data.id}`];
        if (titleInputElement) {
          if (Array.isArray(titleInputElement)) {
            titleInputElement[0].focus();
          } else {
            titleInputElement.focus();
          }
        }
      });
    },

    // 添加子节点
    addNode(parentData) {
      // 生成新节点的唯一ID
      const newId = `node_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
      const newNode = {
        id: newId,
        parentId: parentData.id,
        title: "",
        page: "",
        weidu: "",
        isEdit: true,
        sections: [],
      };

      // 如果父节点没有sections数组，创建一个
      if (!parentData.sections) {
        // Vue 3中直接赋值即可，确保响应式
        parentData.sections = [];
      }

      // 添加新节点到父节点的sections中
      parentData.sections.push(newNode);

      // 设置当前编辑状态
      this.currentName = "";
      this.currentPage = "";

      // 强制刷新视图并等待DOM更新
      this.$forceUpdate();

      this.$nextTick(() => {
        // 等待一小段时间确保DOM完全渲染
        setTimeout(() => {
          // 聚焦到新节点的标题输入框
          const titleInputElement = this.$refs[`title_${newId}`];
          if (titleInputElement) {
            if (Array.isArray(titleInputElement)) {
              titleInputElement[0].focus();
              titleInputElement[0].select();
            } else {
              titleInputElement.focus();
              titleInputElement.select();
            }
          } else {
            console.warn("找不到输入框元素:", `title_${newId}`);
          }
        }, 50);
      });
    },

    // 删除节点
    deleteNode(data) {
      // 删除节点确认
      this.$confirm("是否确认删除该节点?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          // 递归查找并删除节点
          const removeFromTree = (nodes, targetId) => {
            for (let i = 0; i < nodes.length; i++) {
              if (nodes[i].id === targetId) {
                nodes.splice(i, 1);
                return true;
              }
              if (nodes[i].sections && nodes[i].sections.length > 0) {
                if (removeFromTree(nodes[i].sections, targetId)) {
                  return true;
                }
              }
            }
            return false;
          };

          // 从树结构中删除节点
          const deleted = removeFromTree(this.treeData, data.id);

          if (deleted) {
            this.$message({
              type: "success",
              message: "删除成功!",
            });
          } else {
            this.$message({
              type: "error",
              message: "删除失败，请重试!",
            });
          }
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },

    // 处理标题输入失焦
    handleInputBlur(event, data) {
      const inputName = event.target.value.trim();
      if (inputName === "") {
        this.$message({
          type: "warning",
          message: "节点名称不能为空,请重新输入",
        });
        // 如果是新添加的节点且标题为空，删除该节点
        if (!data.title && data.parentId) {
          this.removeEmptyNode(data);
        }
        return;
      }

      // 更新标题
      data.title = inputName;
      // 标题失焦时也使用延迟保存
      this.delayedSaveChanges(data);
    },

    // 清除保存定时器
    clearSaveTimer() {
      if (this.saveTimer) {
        clearTimeout(this.saveTimer);
        this.saveTimer = null;
      }
    },

    // 处理页码输入失焦
    handlePageBlur(event, data) {
      const inputPage = event.target.value.trim();

      // 更新页码（允许为空）
      data.page = inputPage;
      // 页码失焦时不立即保存，给用户时间在两个输入框间切换
      this.delayedSaveChanges(data);
    },

    // 保存节点更改
    saveNodeChanges(data) {
      // 检查标题是否已填写（页码可以为空）
      if (data.title && data.title.trim()) {
        data.isEdit = false;
        this.currentName = "";
        this.currentPage = "";
      }
    },

    // 延迟保存更改，允许在输入框间切换
    delayedSaveChanges(data) {
      // 清除之前的延迟保存
      if (this.saveTimer) {
        clearTimeout(this.saveTimer);
      }

      // 设置延迟保存，给用户时间在标题和页码输入框间切换
      this.saveTimer = setTimeout(() => {
        this.saveNodeChanges(data);
      }, 200);
    },

    // 删除空节点
    removeEmptyNode(nodeToRemove) {
      // 递归查找并删除空节点
      const removeFromTree = (nodes) => {
        for (let i = 0; i < nodes.length; i++) {
          if (nodes[i].id === nodeToRemove.id) {
            nodes.splice(i, 1);
            return true;
          }
          if (nodes[i].sections && nodes[i].sections.length > 0) {
            if (removeFromTree(nodes[i].sections)) {
              return true;
            }
          }
        }
        return false;
      };

      removeFromTree(this.treeData);
      this.currentName = "";
      this.currentPage = "";
    },

    // 过滤选中节点
    filterChecked(checked) {
      // 获得选中集合的node-key
      const indexCodeList = checked.map(function (item) {
        return item.id;
      });

      // 判断node-key是否含有parent-key
      checked.map((value, index) => {
        if (indexCodeList.indexOf(value.parentId) !== -1) {
          checked.splice(index, 1, 1);
        }
      });
      this.treeSelects = checked.filter((value) => {
        return value !== 1;
      });

      // 基于原始treeData来过滤，保持原有顺序
      const filteredTree = this.filterTreeByIds(this.treeData, indexCodeList);
      return filteredTree;
      // return this.treeSelects
    },
    filterTreeByIds(tree, selectedIds) {
      return tree
        .map((node) => {
          // 复制节点对象
          const filteredNode = { ...node };

          // 检查子节点是否存在并递归过滤
          if (filteredNode.sections && filteredNode.sections.length) {
            filteredNode.sections = this.filterTreeByIds(
              filteredNode.sections,
              selectedIds
            );
          }

          // 如果当前节点或其子节点在选中列表中，保留该节点
          if (
            selectedIds.includes(filteredNode.id) ||
            (filteredNode.sections && filteredNode.sections.length)
          ) {
            return filteredNode;
          }

          // 如果当前节点和子节点都不在选中列表中，过滤掉该节点
          return null;
        })
        .filter(Boolean);
    },

    // 确认生成
    confirmGenerate() {
      this.treeSelects = this.$refs.tree.getCheckedNodes();
      const halfCheckedNodes = this.$refs.tree.getHalfCheckedNodes(); // 半选节点
      const allSelectedNodes = [...this.treeSelects, ...halfCheckedNodes];
      const resObj = this.filterChecked(allSelectedNodes);
      console.log("resObj", resObj);
      const queryResObj = { units: [...resObj] };
      const loading = this.$loading({
        lock: true,
        text: "生成中...",
        background: "rgba(0, 0, 0, 0.7)",
      });
      this.$axios
        .post(
          `/tes/course/auto/make/updateDirectoryTree?fileId=${this.fileId}`,
          queryResObj
        )
        .then((res) => {
          if (res.code == 200) {
            this.$message.success(res.message);
            this.$router.push("/aiTeaching/courseBuild/makeKnowledgeBase");
          } else {
            this.$message.error(res.message);
          }
        })
        .finally(() => {
          loading.close();
        });
    },

    // 弹窗重新解析
    resetData() {
      this.dialogAI = false;
      this.treeData = [];
      // 重置全选状态
      this.isSelectAll = false;
      this.isIndeterminate = false;

      if (!this.fileId) {
        this.$message.error("文件信息丢失，请重新选择文件");
        return;
      }

      this.analysisCourse();
    },

    // 处理全选/反选
    handleSelectAll(checked) {
      if (checked) {
        // 全选：获取所有节点的key
        const allKeys = this.getAllNodeKeys(this.treeData);
        this.$refs.tree.setCheckedKeys(allKeys);
      } else {
        // 取消全选
        this.$refs.tree.setCheckedKeys([]);
      }
      this.isIndeterminate = false;
      this.updateSelectAllStatus();
    },

    // 获取所有节点的key
    getAllNodeKeys(nodes) {
      let keys = [];
      nodes.forEach((node) => {
        keys.push(node.id);
        if (node.sections && node.sections.length > 0) {
          keys = keys.concat(this.getAllNodeKeys(node.sections));
        }
      });
      return keys;
    },

    // 更新全选状态
    updateSelectAllStatus() {
      this.$nextTick(() => {
        const checkedNodes = this.$refs.tree.getCheckedNodes();
        const halfCheckedNodes = this.$refs.tree.getHalfCheckedNodes();
        const allKeys = this.getAllNodeKeys(this.treeData);

        if (checkedNodes.length === 0) {
          // 没有选中任何节点
          this.isSelectAll = false;
          this.isIndeterminate = false;
        } else if (checkedNodes.length === allKeys.length) {
          // 全部选中
          this.isSelectAll = true;
          this.isIndeterminate = false;
        } else {
          // 部分选中
          this.isSelectAll = false;
          this.isIndeterminate = true;
        }
      });
    },
  },
};
</script>
<style lang="scss" scoped>
#make-zhinengshibie {
  background-color: #fff;
  padding: 40px 30px;
  box-sizing: border-box;
  min-height: 650px;
  .loading-mask {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 999;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .dot {
      display: flex;
      align-items: center;
      justify-content: center;
      /* 五个点的容器布局 */
      span:not(.loading-text) {
        width: 12px;
        height: 12px;
        background-color: #155eef;
        display: inline-block;
        margin: 0 3px;
        animation: dotPulseWithGradient 1.4s ease-in-out infinite;
      }

      /* 为每个点设置不同的动画延迟 */
      span:nth-child(1) {
        animation-delay: 0s;
      }
      span:nth-child(2) {
        animation-delay: 0.2s;
      }
      span:nth-child(3) {
        animation-delay: 0.4s;
      }
      span:nth-child(4) {
        animation-delay: 0.6s;
      }
      span:nth-child(5) {
        animation-delay: 0.8s;
      }
    }

    .loading-text {
      font-size: 16px;
      color: #fff;
      margin-top: 30px;
      text-align: center;
      line-height: 1.4;
    }
  }

  /* 点的脉冲动画带颜色渐变 */
  @keyframes dotPulseWithGradient {
    0%,
    80%,
    100% {
      transform: scale(0.8);
      opacity: 0.8;
      background-color: #20b6fd;
    }
    40% {
      transform: scale(1.2);
      opacity: 1;
      background-color: #1899fd;
    }
  }
  .step-one {
    .one-title {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 12px;
      .all-title {
        margin-bottom: 0;
      }
    }
    .one-main {
      .avatar-uploader {
        :deep(.el-upload-dragger) {
          border: none;
          padding: 0;
        }
        :deep(.el-upload) {
          width: 100%;
          cursor: pointer;
          position: relative;
          overflow: hidden;
          .upload-box {
            background: #f8f9fb;
            width: 100%;
            border-radius: 10px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 50px 0;
            > div {
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
            }
            .box-one {
              width: 60%;
              border-bottom: 1px solid #efefef;
              gap: 11px;
              padding-bottom: 23px;
              margin-bottom: 29px;
              .img-box {
                margin-bottom: 20px;
              }
              .one-txt {
                font-size: 16px;
                color: #6b6a76;
              }
              .one-subtxt {
                font-size: 14px;
                color: #a2a0b3;
              }
            }
            .box-two {
              .two-txt {
                font-size: 14px;
                color: #5f6feb;
                margin-bottom: 12px;
              }
            }
          }
        }
        margin-bottom: 10px;
      }
      .file-list {
        > div {
          display: flex;
          justify-content: space-between;
          align-items: center;
          height: 70px;
          background: #ffffff;
          border-radius: 10px;
          border: 1px solid #e4e3e4;
          margin-bottom: 10px;
          .file-left {
            display: flex;
            align-items: center;
            gap: 20px;
            padding-left: 54px;
            .file-title {
              font-weight: 400;
              font-size: 14px;
              color: #383743;
            }
          }
          .file-right {
            display: flex;
            align-items: center;
            gap: 40px;
            padding-right: 30px;
            .file-size {
              font-size: 12px;
              color: #676674;
            }
            .file-type {
              width: 100px;
              font-size: 12px;
              color: #676674;
              > div {
                display: flex;
                align-items: center;
                gap: 6px;
              }
            }
            .file-btn {
              display: flex;
              justify-content: center;
              align-items: center;
              gap: 4px;
              width: 95px;
              height: 30px;
              background: #f4f3ff;
              border-radius: 4px;
              font-size: 12px;
              color: #6271bc;
              cursor: pointer;
              &:hover {
                opacity: 0.8;
              }
            }
            .flie-close {
              cursor: pointer;
              &:hover {
                opacity: 0.8;
              }
            }
            /* 禁用状态样式 */
            .disabled-btn {
              cursor: not-allowed; /* 鼠标悬停时显示禁用状态 */
              opacity: 0.6; /* 降低透明度以表示不可点击 */
            }

            .disabled-btn:hover {
            }
          }
        }
      }
    }
  }
  .loading {
    .all-box {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      height: 670px;
      .loading-img {
        animation: bounceai 2s ease-in-out infinite;
      }
      .loading-title {
        font-size: 22px;
        color: #34323d;
        margin: 44px 0 18px 0;
      }
      .loading-subtitle {
        font-size: 16px;
        color: #848387;
      }
    }
    @keyframes bounceai {
      0%,
      100% {
        /* 开始和结束位置 */
        transform: translateY(0px);
      }
      50% {
        /* 中间点位置 */
        transform: translateY(-10px);
      }
    }
  }
  .my-dialog {
    padding: 50px 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 37px;
    .my-dialog-title {
      display: flex;
      align-items: center;
      gap: 10px;
      font-size: 14px;
      color: #727272;
    }
    .my-dialog-content {
      display: flex;
      align-items: center;
      gap: 10px;
    }
  }

  // 目录弹窗
  .dialog-ai {
    .select-all-container {
      display: flex;
      align-items: center;
      padding: 10px 20px;
      border: 1px solid #d8dadd;
      border-bottom: none;
      font-size: 14px;
      color: #333;

      :deep(.el-checkbox) {
        .el-checkbox__label {
          font-size: 14px;
          color: #333;
          font-weight: 500;
        }

        .el-checkbox__input {
          .el-checkbox__inner {
            border-color: #d8dadd;

            &:hover {
              border-color: #457aff;
            }
          }

          &.is-checked .el-checkbox__inner {
            background-color: #457aff;
            border-color: #457aff;
          }

          &.is-indeterminate .el-checkbox__inner {
            background-color: #457aff;
            border-color: #457aff;

            &::before {
              background-color: #fff;
            }
          }
        }
      }
    }

    .tree-container {
      box-sizing: border-box;
      width: 100%;
      height: 558px;
      background: #ffffff;
      border: 1px solid #d8dadd;
      border-top: none;
      padding: 0 20px;
      overflow-y: scroll;

      // 自定义tree节点样式
      :deep(.el-tree-node) {
        .el-tree-node__content {
          position: relative;

          &:hover {
            .custom-tree-node .node-actions {
              opacity: 1;
              visibility: visible;
            }
          }
        }
      }

      .custom-tree-node {
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        position: relative;

        .node-content {
          display: flex;
          align-items: center;
          gap: 10px;
          flex: 1;

          .node-title {
            font-size: 14px;
            color: #333;
          }

          .node-page {
            font-size: 12px;
            color: #666;
            background-color: #f5f5f5;
            padding: 2px 6px;
            border-radius: 3px;
            border: 1px solid #e0e0e0;
          }
        }

        .edit-inputs {
          display: flex;
          align-items: center;
          gap: 8px;
          flex: 1;

          .title-input {
            flex: 1;
            padding: 4px 8px;
            border: 1px solid #d9d9d9;
            border-radius: 3px;
            font-size: 14px;
            outline: none;

            &:focus {
              border-color: #1277eb;
              box-shadow: 0 0 0 2px rgba(18, 119, 235, 0.1);
            }
          }

          .page-input {
            width: 80px;
            padding: 4px 8px;
            border: 1px solid #d9d9d9;
            border-radius: 3px;
            font-size: 14px;
            outline: none;

            &:focus {
              border-color: #1277eb;
              box-shadow: 0 0 0 2px rgba(18, 119, 235, 0.1);
            }
          }
        }

        .node-actions {
          display: flex;
          align-items: center;
          gap: 5px;
          opacity: 0;
          visibility: hidden;
          transition: all 0.3s ease;

          .add-btn,
          .edit-btn,
          .delete-btn {
            background: none;
            border: none;
            font-size: 12px;
            cursor: pointer;
            padding: 2px 8px;
            border-radius: 3px;
            transition: all 0.3s ease;
          }

          .add-btn {
            color: #52c41a;

            &:hover {
              background-color: #f6ffed;
              color: #389e0d;
            }
          }

          .edit-btn {
            color: #1277eb;

            &:hover {
              background-color: #f0f9ff;
              color: #0d66c2;
            }
          }
        }
        .delete-btn {
          color: #f5222d;
          &:hover {
            background-color: #fff1f0;
            color: #ff4d4f;
          }
        }
      }
    }
    .ai-tip {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 16px;
      color: #7a799d;
      .tip {
        display: flex;
        align-items: center;
      }
      .reset {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 10px;
        cursor: pointer;
        span {
          margin-left: 5px !important;
          color: #1277eb;
          font-size: 14px;
        }
      }
    }
    .footer-btn {
      background-color: #fff;
      border: 1px solid #1277eb;
      color: #1277eb;
      &:hover {
        background-color: #1277eb;
        color: #fff;
      }
    }
    &::-webkit-scrollbar {
      width: 4px;
    }
    &::-webkit-scrollbar-thumb {
      background-color: #e5e5e5;
    }
    &::-webkit-scrollbar-track {
      background-color: #f5f5f5;
    }
  }
}
</style>